package main

import (
	"flag"
	"fmt"
	"log"
	"mediumkube/daemon/mgrpc"
	"mediumkube/daemon/tasks"
	"mediumkube/pkg/common/event"
	"mediumkube/pkg/configurations"
	"mediumkube/pkg/procd"
	"mediumkube/pkg/services"
	"net"

	"net/http"
	_ "net/http/pprof"
	"os"
	"os/signal"
	"sync"
	"syscall"

	"google.golang.org/grpc"
	"k8s.io/klog/v2"
)

// DMux used for synchronizing go routines
type DMux struct {
	sync.Mutex
}

var (
	close = make(chan bool)
)

func stopDaemon() {
	close <- true
}

func refreshBridge() {
	config := configurations.Config()
	bridge := config.Bridge
	tasks.ProcessExistence(bridge)
	tasks.ProcessAddr(bridge)
	tasks.ProcessIptables(config)
}

func refreshMesh() {
	config := configurations.Config()
	if config.Overlay.Enabled {
		tasks.StartMesh()
	}
}

func main() {
	taskRunner := procd.NewDaemonRunner()

	tmpFlagSet := flag.NewFlagSet("", flag.ExitOnError)
	profiling := tmpFlagSet.Bool("p", false, "Enable Profiling")
	profilingPort := tmpFlagSet.Int("pport", 7777, "Port of profiling service")
	tmpFlagSet.Parse(os.Args[1:])

	sigChan := make(chan os.Signal)
	signal.Notify(sigChan, os.Interrupt)
	signal.Notify(sigChan, syscall.SIGTERM)

	eventBus := event.GetEventBus()
	wg := sync.WaitGroup{}

	sigHandler := func() {
		wg.Add(1)
		defer wg.Done()
		sig := <-sigChan
		klog.Info("Sig recvd: ", sig)
		stopDaemon()
	}

	taskRunner.Submit(tasks.NewDNSMasqTask(configurations.Config()))
	taskRunner.Submit(tasks.NewEtcdTask())
	taskRunner.Submit(tasks.NewFlannelTask())
	taskRunner.Submit(tasks.NewDHCPTask())
	taskRunner.Schedule(procd.NewScheduledTask(refreshBridge, 3000))
	taskRunner.Schedule(procd.NewScheduledTask(refreshMesh, 3000))

	profiler := func() {

		klog.Infof("Profiling service starting on localhost:%v/debug/pprof", *profilingPort)
		log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", *profilingPort), nil))
	}

	config := configurations.Config()
	go sigHandler()
	if *profiling {
		go profiler()
	}

	svr := grpc.NewServer()
	klog.Info("Registering Mediumkube server")
	mgrpc.RegisterDomainSerciceServer(svr, mgrpc.NewServer(config))
	lis, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", config.Overlay.GRPCPort))
	if err != nil {
		klog.Error("Failed to start grpc server", err)
		stopDaemon()
	}

	go svr.Serve(lis)
	if err != nil {
		klog.Error("Failed to start grpc server", err)
		stopDaemon()
	}

	go taskRunner.Start()

	finished := false
	for !finished {

		select {
		case <-eventBus.DomainUpdate:
			tasks.SyncDomain(config)
		case <-close:
			taskRunner.Terminate()
			tasks.CleanUpIptables()
			services.GetDomainManager(config.Backend).Disconnect()
			finished = true
		}
	}

	klog.Info("Daemon exited")
}
